#pragma once

#ifndef __CR_CORE_FILE_SYSTEM_PATH_H__
#define __CR_CORE_FILE_SYSTEM_PATH_H__

#include "crPathTraits.h"

NS_CR_BEGIN

namespace filesystem
{
	class path
	{
	public:
#ifdef _WIN32
		typedef wchar_t					value_type;
		static const value_type			preferred_separator = L'\\';
#else
		typedef char					value_type;
		static constexpr value_type		preferred_separator = '/';
#endif
		typedef _STD basic_string<value_type>					string_type;
		typedef _STD codecvt<wchar_t, char, _STD mbstate_t>		codecvt_type;

		path() = default;
		path(const path& p) 
			: _pathname(p._pathname)
		{ }

		template <class Source>
		path(Source const& source,
			typename enable_if<path_traits::is_pathable<
			typename _STD decay<Source>::type> >::type* = 0)
		{
			path_traits::dispatch(source, _pathname);
		}


		path(const value_type* s) 
			: _pathname(s)
		{}
		path(value_type* s) 
			: _pathname(s) 
		{}
		path(const string_type& s) 
			: _pathname(s) 
		{}
		path(string_type& s)
			: _pathname(s) 
		{}

		template <class Source>
		path(Source const& source, const codecvt_type& cvt)
		{
			path_traits::dispatch(source, _pathname, cvt);
		}

		template <class InputIterator>
		path(InputIterator begin, InputIterator end)
		{
			if (begin != end)
			{
				_STD basic_string<typename _STD iterator_traits<InputIterator>::value_type>
					s(begin, end);
				path_traits::convert(s.c_str(), s.c_str() + s.size(), _pathname);
			}
		}

		template <class InputIterator>
		path(InputIterator begin, InputIterator end, const codecvt_type& cvt)
		{
			if (begin != end)
			{
				_STD basic_string<typename std::iterator_traits<InputIterator>::value_type>
					s(begin, end);
				path_traits::convert(s.c_str(), s.c_str() + s.size(), m_pathname, cvt);
			}
		}

		path& operator=(const path& p)
		{
			_pathname = p._pathname;
			return *this;
		}

		template <class Source>
		typename enable_if<path_traits::is_pathable<typename _STD decay<Source>::type>, path&>::type
			operator=(Source const& source)
		{
			_pathname.clear();
			path_traits::dispatch(source, _pathname);
			return *this;
		}

		path& operator=(const value_type* ptr)
		{
			_pathname = ptr;
			return *this;
		}

		path& operator=(value_type* ptr)
		{
			_pathname = ptr;
			return *this;
		}

		path& operator=(const string_type& s)
		{
			_pathname = s;
			return *this;
		}

		path& operator=(string_type& s)
		{
			_pathname = s;
			return *this;
		}

		path& assign(const value_type* ptr, const codecvt_type&)
		{
			_pathname = ptr;
			return *this;
		}

		template <class Source>
		path& assign(Source const& source, const codecvt_type& cvt)
		{
			_pathname.clear();
			path_traits::dispatch(source, _pathname, cvt);
			return *this;
		}

		template <class InputIterator>
		path& assign(InputIterator begin, InputIterator end)
		{
			_pathname.clear();
			if (begin != end)
			{
				std::basic_string<typename std::iterator_traits<InputIterator>::value_type>
					s(begin, end);
				path_traits::convert(s.c_str(), s.c_str() + s.size(), _pathname);
			}
			return *this;
		}

		template <class InputIterator>
		path& assign(InputIterator begin, InputIterator end, const codecvt_type& cvt)
		{
			_pathname.clear();
			if (begin != end)
			{
				std::basic_string<typename std::iterator_traits<InputIterator>::value_type>
					s(begin, end);
				path_traits::convert(s.c_str(), s.c_str() + s.size(), _pathname, cvt);
			}
			return *this;
		}

		template <class Source>
		typename enable_if<path_traits::is_pathable<typename _STD decay<Source>::type>, path&>::type
			operator+=(Source const& source)
		{
			return concat(source);
		}

		path& operator+=(const path& p) 
		{
			_pathname += p._pathname;
			return *this; 
		}
		path& operator+=(const value_type* ptr) 
		{ 
			_pathname += ptr;
			return *this; 
		}
		path& operator+=(value_type* ptr) 
		{ 
			_pathname += ptr;
			return *this;
		}
		path& operator+=(const string_type& s) 
		{ 
			_pathname += s;
			return *this; 
		}
		path& operator+=(string_type& s) 
		{
			_pathname += s;
			return *this;
		}
		path& operator+=(value_type c) 
		{
			_pathname += c;
			return *this;
		}

		template <class CharT>
		typename enable_if<_STD is_integral<CharT>, path&>::type
			operator+=(CharT c)
		{
			CharT tmp[2];
			tmp[0] = c;
			tmp[1] = 0;
			return concat(tmp);
		}

		template <class Source>
		path& concat(Source const& source)
		{
			path_traits::dispatch(source, _pathname);
			return *this;
		}

		template <class Source>
		path& concat(Source const& source, const codecvt_type& cvt)
		{
			path_traits::dispatch(source, _pathname, cvt);
			return *this;
		}

		template <class InputIterator>
		path& concat(InputIterator begin, InputIterator end)
		{
			if (begin == end)
				return *this;
			std::basic_string<typename std::iterator_traits<InputIterator>::value_type>
				s(begin, end);
			path_traits::convert(s.c_str(), s.c_str() + s.size(), _pathname);
			return *this;
		}

		template <class InputIterator>
		path& concat(InputIterator begin, InputIterator end, const codecvt_type& cvt)
		{
			if (begin == end)
				return *this;
			std::basic_string<typename std::iterator_traits<InputIterator>::value_type>
				s(begin, end);
			path_traits::convert(s.c_str(), s.c_str() + s.size(), _pathname, cvt);
			return *this;
		}

		path& operator/=(const path& p);

		template <class Source>
		typename enable_if<path_traits::is_pathable<typename _STD decay<Source>::type>, path&>::type
			operator/=(Source const& source)
		{
			return append(source);
		}

		path& operator/=(const value_type* ptr);
		path& operator/=(value_type* ptr)
		{
			return this->operator/=(const_cast<const value_type*>(ptr));
		}
		path& operator/=(const string_type& s) 
		{
			return this->operator/=(path(s));
		}
		path& operator/=(string_type& s) 
		{
			return this->operator/=(path(s));
		}

		path& append(const value_type* ptr)
		{
			this->operator/=(ptr);
			return *this;
		}

		path& append(const value_type* ptr, const codecvt_type&)
		{
			this->operator/=(ptr);
			return *this;
		}


		template <class Source>
		path& append(Source const& source);

		template <class Source>
		path& append(Source const& source, const codecvt_type& cvt);

		template <class InputIterator>
		path& append(InputIterator begin, InputIterator end);

		template <class InputIterator>
		path& append(InputIterator begin, InputIterator end, const codecvt_type& cvt);

		void clear() 
		{
			_pathname.clear(); 
		}
		path&  make_preferred();
		path&  remove_filename();
		path&  replace_extension(const path& new_extension = path());
		void   swap(path& rhs) 
		{
			_pathname.swap(rhs._pathname);
		}

		const string_type&  native() const 
		{
			return _pathname; 
		}  
		const value_type* c_str() const
		{ 
			return _pathname.c_str(); 
		}

		template <class String>
		String string() const;

		template <class String>
		String string(const codecvt_type& cvt) const;

#if _WIN32
		const std::string string() const
		{
			std::string tmp;
			if (!_pathname.empty())
				path_traits::convert(&*_pathname.begin(), &*_pathname.begin() + _pathname.size(),
					tmp);
			return tmp;
		}

		const std::string string(const codecvt_type& cvt) const
		{
			std::string tmp;
			if (!_pathname.empty())
				path_traits::convert(&*_pathname.begin(), &*_pathname.begin() + _pathname.size(),
					tmp, cvt);
			return tmp;
		}
		const std::wstring&  wstring() const { return _pathname; }
		const std::wstring&  wstring(const codecvt_type&) const { return _pathname; }
#else
		const std::string&  string() const { return _pathname; }
		const std::string&  string(const codecvt_type&) const { return _pathname; }

		const std::wstring  wstring() const
		{
			std::wstring tmp;
			if (!m_pathname.empty())
				path_traits::convert(&*_pathname.begin(), &*_pathname.begin() + _pathname.size(),
					tmp);
			return tmp;
		}
		const std::wstring  wstring(const codecvt_type& cvt) const
		{
			std::wstring tmp;
			if (!_pathname.empty())
				path_traits::convert(&*_pathname.begin(), &*_pathname.begin() + _pathname.size(),
					tmp, cvt);
			return tmp;
		}
#endif

		template <class String>
		String generic_string() const;

		template <class String>
		String generic_string(const codecvt_type& cvt) const;

#if _WIN32
		const std::string   generic_string() const;
		const std::string   generic_string(const codecvt_type& cvt) const;
		const std::wstring  generic_wstring() const;
		const std::wstring  generic_wstring(const codecvt_type&) const { return generic_wstring(); };
#else
		const std::string&  generic_string() const { return m_pathname; }
		const std::string&  generic_string(const codecvt_type&) const { return m_pathname; }
		const std::wstring  generic_wstring() const { return wstring(); }
		const std::wstring  generic_wstring(const codecvt_type& cvt) const { return wstring(cvt); }

#endif

		int compare(const path& p) const NOEXCEPT;  // generic, lexicographical
		int compare(const std::string& s) const { return compare(path(s)); }
		int compare(const value_type* s) const { return compare(path(s)); }


		path  root_path() const;
		path  root_name() const;         // returns 0 or 1 element path
										 // even on POSIX, root_name() is non-empty() for network paths
		path  root_directory() const;    // returns 0 or 1 element path
		path  relative_path() const;
		path  parent_path() const;
		path  filename() const;          // returns 0 or 1 element path
		path  stem() const;              // returns 0 or 1 element path
		path  extension() const;         // returns 0 or 1 element path

		bool empty() const { return _pathname.empty(); } // name consistent with std containers
		bool has_root_path() const { return has_root_directory() || has_root_name(); }
		bool has_root_name() const { return !root_name().empty(); }
		bool has_root_directory() const { return !root_directory().empty(); }
		bool has_relative_path() const { return !relative_path().empty(); }
		bool has_parent_path() const { return !parent_path().empty(); }
		bool has_filename() const { return !_pathname.empty(); }
		bool has_stem() const { return !stem().empty(); }
		bool has_extension() const { return !extension().empty(); }
		bool is_absolute() const
		{
#     ifdef _WIN32
			return has_root_name() && has_root_directory();
#     else
			return has_root_directory();
#     endif
		}
		bool is_relative() const { return !is_absolute(); }

		class iterator;
		typedef iterator const_iterator;

		iterator begin() const;
		iterator end() const;

		static std::locale  imbue(const std::locale& loc);
		static const        codecvt_type& codecvt();

		string_type _pathname;
		
// 		string_type::size_type m_append_separator_if_needed();
// 		
// 		void m_erase_redundant_separator(string_type::size_type sep_pos);
// 		string_type::size_type m_parent_path_end() const;
// 
// 		path& m_normalize();
// 		
// 		friend class iterator;
// 		friend bool operator<(const path& lhs, const path& rhs);
// 
// 		static void m_path_iterator_increment(path::iterator & it);
// 		static void m_path_iterator_decrement(path::iterator & it);
	};

	//------------------------------------------------------------------------------------//
	//                             class path::iterator                                   //
	//------------------------------------------------------------------------------------//
// 	
// 	class path::iterator
// 		: public _STD iterator< 
// 			_STD bidirectional_iterator_tag,
// 			typename path::string_type,
// 			_STD ptrdiff_t,
// 			typename path::string_type *,
// 			typename path::string_type&>
// 	{
// 	public:
// 		typedef path::iterator	_Myt;
// 		typedef path			path_type;
// 		typedef typename path::string_type string_type;
// 		typedef const typename path::string_type *pointer;
// 		typedef const typename path::string_type& reference;
// 
// 		iterator()
// 			: _Myptr(0)
// 			, _Myoff(0)
// 		{}
// 
// 		iterator(const path_type& _Pval, _STD size_t _Off)
// 			: _Myptr(&_Pval)
// 			, _Myoff(_Off)
// 		{
// 			_Getval();
// 		}
// 
// 		iterator(const iterator& _Right)
// 			: _Myptr(_Right._Myptr)
// 			, _Myelem(_Right._Myelem)
// 			, _Myoff(_Right._Myoff)
// 		{	
// 		}
// 
// 		iterator& operator=(const iterator& _Right)
// 		{	// copy assign
// 			_Myptr = _Right._Myptr;
// 			_Myelem = _Right._Myelem;
// 			_Myoff = _Right._Myoff;
// 			return (*this);
// 		}
// 
// 		iterator(iterator&& _Right)
// 			: _Myptr(_Right._Myptr)
// 			, _Myelem(_STD move(_Right._Myelem))
// 			, _Myoff(_Right._Myoff)
// 		{	// move construct
// 		}
// 
// 		iterator& operator=(iterator&& _Right)
// 		{	// move assign
// 			_Myptr = _Right._Myptr;
// 			_Myelem = _STD move(_Right._Myelem);
// 			_Myoff = _Right._Myoff;
// 			return (*this);
// 		}
// 
// 		reference operator*() const
// 		{	// return designated value
// 			return (_Myelem);
// 		}
// 
// 		pointer operator->() const
// 		{	// return pointer to class object
// 			return (_STD pointer_traits<pointer>::pointer_to(**this));
// 		}
// 
// 		_Myt& operator++()
// 		{
// #pragma warning(push)
// #pragma warning(disable: 6011)	
// 
// #if _ITERATOR_DEBUG_LEVEL == 2
// 			if (_Myptr == 0 || _Myptr->string().size() <= _Myoff)
// 				_DEBUG_ERROR(
// 					"basic_path<T>::iterator is not incrementable");
// 
// #elif _ITERATOR_DEBUG_LEVEL == 1
// 			_SCL_SECURE_VALIDATE(_Myptr != 0 && _Myoff < _Myptr->_Mystr.size());
// #endif /* _ITERATOR_DEBUG_LEVEL */
// 
// 			size_t _Pend = _Myptr->_Prefix_end();
// 			size_t _Size = _Myptr->_Mystr.size();
// 
// 			if (_Myoff < _Pend)
// 				_Myoff = _Pend;	// point past "x:"
// 			else if (_Myoff == _Pend && _Pend < _Size
// 				&& _Myptr->_Mystr[_Pend] == slash<path_type>::value)
// 			{	// point past root "/" and following slashes
// 				for (++_Myoff; _Myoff < _Size; ++_Myoff)
// 					if (_Myptr->_Mystr[_Myoff]
// 						!= slash<path_type>::value)
// 						break;
// 			}
// 			else
// 			{	// point past slashes followed by stuff
// 				for (; _Myoff < _Size; ++_Myoff)
// 					if (_Myptr->_Mystr[_Myoff]
// 						!= slash<path_type>::value)
// 						break;
// 				for (; _Myoff < _Size; ++_Myoff)
// 					if (_Myptr->_Mystr[_Myoff]
// 						== slash<path_type>::value)
// 						break;
// 			}
// 			_Getval();
// 			return (*this);
// 
// #pragma warning(pop)
// 		}
// 	private:
// 		const path_type*	 _Myptr;
// 		string_type			_Myelem;
// 		_STD size_t			_Myoff;
// 	};


	//------------------------------------------------------------------------------------//
	//                                                                                    //
	//                              non-member functions                                  //
	//                                                                                    //
	//------------------------------------------------------------------------------------//


	INLINE std::size_t hash_value(const path& x)
	{
# ifdef _WIN32
		std::size_t seed = 0;
		for (const path::value_type* it = x.c_str(); *it; ++it)
			hash_combine(seed, *it == '/' ? L'\\' : *it);
		return seed;
# else   
		return hash_range(x.native().begin(), x.native().end());
# endif
	}

	INLINE path operator/(const path& lhs, const path& rhs) { return path(lhs) /= rhs; }
}

NS_CR_END

#endif // !__CR_CORE_FILE_SYSTEM_PATH_H__
